# -*- coding: utf-8 -*-
# @Time    : 2024/7/1 14:25
# @Author  : yujiahao
# @File    : 09_numpy_math_func.py
# @description: NumPy 数学函数示例

import numpy as np

"""
NumPy 数学函数示例：

NumPy 提供了丰富的数学函数，用于执行各种数学运算。这些函数被定义在 numpy 模块中，主要用于处理数组。

基本数学函数：
1. add() - 对数组元素逐个求和。
2. subtract() - 对数组元素逐个求差。
3. multiply() - 对数组元素逐个相乘。
4. divide() - 对数组元素逐个相除。
5. power() - 对数组元素逐个求幂。
6. mod() - 对数组元素逐个求余数。
7. sqrt() - 对数组元素逐个求平方根。

统计函数：
1. amin() 和 amax() - 计算数组沿指定轴的最小值与最大值。
2. ptp() - 计算数组元素中最大值与最小值的差值。
3. percentile() - 计算数组中任意百分比分位数。
4. median() - 计算数组元素的中位数。
5. mean() - 计算数组元素的算术平均值。
6. average() - 计算数组元素的加权平均值。
7. var() - 计算数组元素的方差。
8. std() - 计算数组元素的标准差。
9. min() - 计算数组元素的最小值。
10. max() - 计算数组元素的最大值。
11. sum() - 计算数组元素的总和。
12. prod() - 计算数组元素的乘积。

三角函数：
1. sin() - 计算数组元素的正弦值。
2. cos() - 计算数组元素的余弦值。
3. tan() - 计算数组元素的正切值。
4. arcsin() - 计算数组元素的反正弦值。
5. arccos() - 计算数组元素的反余弦值。
6. arctan() - 计算数组元素的反正切值。

指数和对数函数：
1. exp() - 计算数组元素的指数值。
2. log() - 计算数组元素的自然对数。
3. log10() - 计算数组元素的以10为底的对数。
4. log2() - 计算数组元素的以2为底的对数。

舍入函数：
1. around() - 将数组中的数字四舍五入到指定的小数位数。
2. floor() - 对数组中的每个元素向下取整。
3. ceil() - 对数组中的每个元素向上取整。

示例代码：
"""

# 基本数学函数
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])

print("基本数学函数:")

# 1. add() - 对数组元素逐个求和
# 用法: np.add(array1, array2)
result = np.add(a, b)
print("加法结果:", result)

# 2. subtract() - 对数组元素逐个求差
# 用法: np.subtract(array1, array2)
result = np.subtract(a, b)
print("减法结果:", result)

# 3. multiply() - 对数组元素逐个相乘
# 用法: np.multiply(array1, array2)
result = np.multiply(a, b)
print("乘法结果:", result)

# 4. divide() - 对数组元素逐个相除
# 用法: np.divide(array1, array2)
result = np.divide(a, b)
print("除法结果:", result)

# 5. power() - 对数组元素逐个求幂
# 用法: np.power(array, exponent)
result = np.power(a, 2)
print("幂运算结果:", result)

# 6. mod() - 对数组元素逐个求余数
# 用法: np.mod(array, divisor)
result = np.mod(a, 2)
print("取余结果:", result)

# 7. sqrt() - 对数组元素逐个求平方根
# 用法: np.sqrt(array)
result = np.sqrt(a)
print("平方根结果:", result)

# 统计函数
print("\n统计函数:")

# amin() 和 amax() 示例
# amin() 用于计算数组沿指定轴的最小值
# amax() 用于计算数组沿指定轴的最大值
a = np.array([[3, 7, 5], [8, 4, 3], [2, 4, 9]])
print("数组 a 是：")
print(a)
print("调用 amin() 函数：", np.amin(a))
print("调用 amin(axis=1) 函数：", np.amin(a, axis=1))
print("调用 amax() 函数：", np.amax(a))
print("调用 amax(axis=0) 函数：", np.amax(a, axis=0))

# ptp() 示例
# ptp() 用于计算数组元素中最大值与最小值的差值
b = np.array([[2, 10, 20], [80, 43, 31], [22, 43, 10]])
print("\n数组 b 是：")
print(b)
print("调用 ptp(axis=1) 函数：", np.ptp(b, axis=1))
print("调用 ptp(axis=0) 函数：", np.ptp(b, axis=0))

# percentile() 示例
# percentile() 用于计算数组中任意百分比分位数
'''
numpy.percentile() 是 NumPy 库中的一个函数，用于计算数组中指定百分位数的值。百分位数是统计学中的一个度量，用于描述数据分布的特定位置。
比如，第 𝑝百分位数表示数据中有 𝑝% 的值小于或等于这个值。
百分位数描述的是数据分布中的一个特定位置的值，而不是几个元素的和。
计算百分位数时，需要先对数据排序，然后根据百分位数的位置进行插值计算。
函数签名
    numpy.percentile(a, q, axis=None, out=None, overwrite_input=False, interpolation='linear', keepdims=False)
参数解释
    a: 输入数组，可以是一维或多维数组。
    q: 要计算的百分位数，可以是一个数值或一维数组。取值范围是 [0, 100]。
    axis: 沿着该轴计算百分位数。如果为 None，则在整个数组上计算百分位数。常见的取值有：
    axis=0：沿着每列计算百分位数。
    axis=1：沿着每行计算百分位数。
    out: 可选。用于存储输出结果的数组，必须具有正确的形状和类型。
    overwrite_input: 可选。如果为 True，允许修改输入数组 a，以减少内存使用。默认值为 False。
    interpolation: 可选。指定插值方法，默认值为 'linear'。其他可选值包括 'lower'、'higher'、'nearest' 和 'midpoint'。
    keepdims: 可选。如果为 True，则保持原始数组的维度。
返回值
    返回计算得到的百分位数值。如果 q 是一个数值，则返回一个标量；如果 q 是一个数组，则返回一个与 q 具有相同形状的数组。间；
    axis：沿着指定的轴计算百分位数。

计算过程详解：

数组 a:
[[ 2 10 20]
 [80 43 31]
 [22 43 10]]

沿着 axis=0 计算第 10 百分位数表示在每一列上计算第 10 百分位数。

第 1 列：[2, 80, 22]
- 排序后：[2, 22, 80]
- 第 10 百分位数的位置是 10% * (n-1) = 0.1 * (3-1) = 0.2
- 第 10 百分位数的值是 2 + 0.2 * (22 - 2) = 2 + 4 = 4

第 2 列：[10, 43, 43]
- 排序后：[10, 43, 43]
- 第 10 百分位数的位置是 10% * (n-1) = 0.1 * (3-1) = 0.2
- 第 10 百分位数的值是 10 + 0.2 * (43 - 10) = 10 + 6.6 = 13.3

第 3 列：[20, 31, 10]
- 排序后：[10, 20, 31]
- 第 10 百分位数的位置是 10% * (n-1) = 0.1 * (3-1) = 0.2
- 第 10 百分位数的值是 10 + 0.2 * (20 - 10) = 10 + 2 = 12

所以，沿着 axis=0 计算第 10 百分位数的结果是：
[ 4.  13.3 12.]


'''
c = np.array([[2, 10, 20], [80, 43, 31], [22, 43, 10]])
print("\n数组 c 是：")
print(c)
print("调用 percentile(axis=0) 函数：", np.percentile(c, 10, axis=0))
print("调用 percentile(axis=1) 函数：", np.percentile(c, 10, axis=1))

# median() 示例
# median() 用于计算数组元素的中位数
d = np.array([[30, 65, 70], [80, 95, 10], [50, 90, 60]])
print("\n数组 d 是：")
print(d)
print("调用 median() 函数：", np.median(d))
print("调用 median(axis=0) 函数：", np.median(d, axis=0))
print("调用 median(axis=1) 函数：", np.median(d, axis=1))

# mean() 示例
# mean() 用于计算数组元素的算术平均值
e = np.array([[1, 2, 3], [3, 4, 5], [4, 5, 6]])
print("\n数组 e 是：")
print(e)
print("调用 mean() 函数：", np.mean(e))
print("调用 mean(axis=0) 函数：", np.mean(e, axis=0))
print("调用 mean(axis=1) 函数：", np.mean(e, axis=1))

# average() 示例
# average() 用于计算数组元素的加权平均值
f = np.array([1, 2, 3, 4])
weights = np.array([4, 3, 2, 1])
print("\n数组 f 是：")
print(f)
print("调用 average() 函数（无权重）：", np.average(f))
print("调用 average() 函数（有权重）：", np.average(f, weights=weights))
print("调用 average() 函数（有权重，返回权重和）：", np.average(f, weights=weights, returned=True))

# 多维数组 average() 示例

'''
加权平均值是将数组中各数值乘以相应的权数，然后再对权重值求总和，最后以权重的总和除以总的单位数（即因子个数）。

numpy.average() 根据在数组中给出的权重，计算数组元素的加权平均值。该函数可以接受一个轴参数 axis，如果未指定，则数组被展开为一维数组。

下面举一个简单的示例：现有数组 [1,2,3,4] 和相应的权重数组 [4,3,2,1]，它的加权平均值计算如下：
加权平均值=（1 * 4 + 2 * 3 + 3 * 2 + 4 * 1）/（4 + 3 + 2 + 1）

'''

# 一维数组
a = np.array([1, 2, 3, 4])
print('\na数组是：\n')
print(a)
# average()函数：
print(np.average(a))
# 若不指定权重相当于对数组求均值
we = np.array([4, 3, 2, 1])
# 调用 average() 函数：')
print(np.average(a, weights=we))
# returned 为Ture，则返回权重的和
print(' returned 为Ture，则返回权重的和', np.average([1, 2, 3, 4], weights=[4, 3, 2, 1], returned=True))

# 多维数组
g = np.arange(6).reshape(3, 2)
weights_2d = np.array([3, 5])
print("\n多维数组 g 是：")
print(g)
print("调用 average(axis=1) 函数：", np.average(g, axis=1, weights=weights_2d))
print("调用 average(axis=1, returned=True) 函数：", np.average(g, axis=1, weights=weights_2d, returned=True))

# var() 示例
# var() 用于计算数组元素的方差
print("\n调用 var() 函数：", np.var([1, 2, 3, 4]))

# std() 示例
# std() 用于计算数组元素的标准差
print("调用 std() 函数：", np.std([1, 2, 3, 4]))

# 统计函数（继续）
# min() 用于计算数组元素的最小值
print("最小值:", np.min(a))
# max() 用于计算数组元素的最大值
print("最大值:", np.max(a))
# sum() 用于计算数组元素的总和
print("总和:", np.sum(a))
# prod() 用于计算数组元素的乘积
print("乘积:", np.prod(a))

# 三角函数
print("\n三角函数:")

# 1. sin() - 计算数组元素的正弦值
# 用法: np.sin(array)
result = np.sin(a)
print("正弦值:", result)

# 2. cos() - 计算数组元素的余弦值
# 用法: np.cos(array)
result = np.cos(a)
print("余弦值:", result)

# 3. tan() - 计算数组元素的正切值
# 用法: np.tan(array)
result = np.tan(a)
print("正切值:", result)

# 4. arcsin() - 计算数组元素的反正弦值
# 用法: np.arcsin(array)
# 需要将值限制在 [-1, 1] 范围内
result = np.arcsin(np.clip(a / 4, -1, 1))
print("反正弦值:", result)

# 5. arccos() - 计算数组元素的反余弦值
# 用法: np.arccos(array)
# 需要将值限制在 [-1, 1] 范围内
result = np.arccos(np.clip(a / 4, -1, 1))
print("反余弦值:", result)

# 6. arctan() - 计算数组元素的反正切值
# 用法: np.arctan(array)
result = np.arctan(a)
print("反正切值:", result)

# 角度转换后的三角函数值
# 计算 arr 数组中给定角度的三角函数值
# 通过乘以 np.pi/180 将其转换为弧度
arr = np.array([0, 30, 60, 90, 120, 150, 180])
print("\n角度转换后的三角函数值:")

# 将角度转换为弧度后计算三角函数值
print("sin() 正弦值:", np.sin(arr * np.pi / 180))
print("cos() 余弦值:", np.cos(arr * np.pi / 180))
print("tan() 正切值:", np.tan(arr * np.pi / 180))

# 反三角函数
arr = np.array([0, 30, 60, 90])
sinval = np.sin(arr * np.pi / 180)
print("\n反三角函数:")

# 计算反三角函数值并转换为角度验证
print("正弦值数组:", sinval)
print("反正弦值 (弧度):", np.arcsin(sinval))
print("反正弦值 (角度):", np.degrees(np.arcsin(sinval)))

cosval = np.cos(arr * np.pi / 180)
print("余弦值数组:", cosval)
print("反余弦值 (弧度):", np.arccos(cosval))
print("反余弦值 (角度):", np.degrees(np.arccos(cosval)))

tanval = np.tan(arr * np.pi / 180)
print("正切值数组:", tanval)
print("反正切值 (弧度):", np.arctan(tanval))
print("反正切值 (角度):", np.degrees(np.arctan(tanval)))

# 舍入函数
arr = np.array([12.202, 90.23120, 123.020, 23.202])
print("\n舍入函数:")

# 1. around() - 将数组中的数字四舍五入到指定的小数位数
# 用法: np.around(array, decimals)
print("原数组 arr:", arr)
print("数组值四舍五入到小数点后两位:", np.around(arr, 2))
print("数组值四舍五入到小数点后 -1 位:", np.around(arr, -1))

# 2. floor() - 对数组中的每个元素向下取整
# 用法: np.floor(array)
a = np.array([-1.8, 1.1, -0.4, 0.9, 18])
print("对数组 a 向下取整:", np.floor(a))

# 3. ceil() - 对数组中的每个元素向上取整
# 用法: np.ceil(array)
print("对数组 a 向上取整:", np.ceil(a))

# 指数和对数函数
print("\n指数和对数函数:")

# 1. exp() - 计算数组元素的指数值
# 用法: np.exp(array)
result = np.exp(a)
print("指数值:", result)

# 2. log() - 计算数组元素的自然对数
# 用法: np.log(array)
# 对数运算前进行预处理，确保数组中的所有值都是正数
safe_a = np.where(a <= 0, np.nan, a)
print("自然对数:", np.log(safe_a))

# 3. log10() - 计算数组元素的以10为底的对数
# 用法: np.log10(array)
print("以10为底的对数:", np.log10(safe_a))

# 4. log2() - 计算数组元素的以2为底的对数
# 用法: np.log2(array)
print("以2为底的对数:", np.log2(safe_a))
